package com.pudu.pudusclient;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.pudu.pdrobot.App;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;

public class ControlClientService extends Service {

    private String THIS_JAVA = "ControlClientService";

    public static int SERVERPORT = 12345;
    public String SERVERIP = "";

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.i(App.MYTAG, "ControlClientService.onCreate");
        return binder;
    }

    private IBinder binder = new ClientServiceBinder();

    public class ClientServiceBinder extends Binder {
        public ControlClientService getService() {
            return ControlClientService.this;
        }
    }

    @Override
    public void onCreate() {
        Log.i(App.MYTAG, "ControlClientService.onCreate");
    }

    Handler myHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x1:
                    if (mcallback != null) {
                        mcallback.ConnectCallback(true);
                    }
                    break;
                case 0x2:
                    if (mcallback != null) {
                        mcallback.ConnectCallback(false);
                    }
                    break;
                case 0x3:
                    if (mcallback != null) {
                        mcallback.DisconnectCallback();
                    }
                    break;

                case 0x4:
                    if (mcallback != null) {
                        byte[] data = (byte[]) msg.obj;
                        if(data != null)
                            mcallback.ReceiveCallback(data);
                        if(data.length != 8)
                            Log.i("PUDUROBOT","data size:"+ Arrays.toString(data)+data.length);
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(App.MYTAG, "ControlClientService.onDestroy");
        super.onDestroy();
    }

    public static interface ConnectCallback {
        public void ConnectCallback(boolean bRet);

        public void DisconnectCallback();

        public void ReceiveCallback(byte[] data);
    }

    private Socket mySocket;
    HandlerThread mclientThread;
    Handler mClienthandler;
    Thread threadreceive;
    Thread threadsend;
    Runnable Receiverunnal;
    clientSendRunnable Sendrunnale;
    ConnectCallback mcallback;
    boolean bWorking = false;

    public void ConnectToServer(final String ip, final int port, final ConnectCallback callback) {
        mcallback = callback;

        if(bWorking == true)
        {
            myHandler.sendEmptyMessage(0x02);
            return;
        }

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    mySocket = new Socket();
                    SocketAddress addr = new InetSocketAddress(ip, port);
                    mySocket.connect(addr, 2000);
                    mySocket.setSoTimeout(1000);

                    MyCallback mycallback = new MyCallback();
                    mclientThread = new HandlerThread("ClientSocketThread");
                    mclientThread.start();
                    mClienthandler = new Handler(mclientThread.getLooper(), mycallback);
                    Receiverunnal = new clientReceiveRunnable(mySocket);
                    threadreceive = new Thread(Receiverunnal);
                    threadreceive.start();
                    Sendrunnale = new clientSendRunnable(mySocket);
                    threadsend = new Thread(Sendrunnale);
                    threadsend.start();

                    synchronized(this) {
                        bWorking = true;
                    }
                    myHandler.sendEmptyMessage(0x01);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    myHandler.sendEmptyMessage(0x02);

                }
            }
        }).start();
    }

    public void StopClientWork()
    {
        try {
            if (bWorking == false) {
                myHandler.sendEmptyMessage(0x03);
                return;
            }

            if (mClienthandler != null) {
                Message msg = mClienthandler.obtainMessage();
                msg.what = 0x02;
                mClienthandler.sendMessage(msg);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }

        return;
    }


    class clientReceiveRunnable implements Runnable {
        Socket mSocket;

        clientReceiveRunnable(Socket socket) {
            mSocket = socket;
        }

        @Override
        public void run() {

            InputStream inputReader = null;

            while (mSocket.isConnected() && !Thread.interrupted()) {

                try {
                    byte[] result = new byte[8];
                    inputReader = mSocket.getInputStream();
                    int len = inputReader.read(result);
                    Log.i(THIS_JAVA,"receive buff:"+Arrays.toString(result));
                    if(len == 8) {
                        Message msg = myHandler.obtainMessage();
                        msg.what = 0x04;
                        msg.obj = result;
                        myHandler.sendMessage(msg);
                    }

                } catch (SocketTimeoutException e) {
                    e.printStackTrace();
                    continue;
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }

            Message msg = mClienthandler.obtainMessage();
            msg.what = 0x02;
            mClienthandler.sendMessage(msg);
            Log.i("aaaa", "ReceiveRunnable quit");

        }
    }

    class clientSendRunnable implements Runnable {
        Socket mSocket;

        private LinkedBlockingQueue<MySocketPacket> sendingPacketQueue;

        clientSendRunnable(Socket socket) {
            mSocket = socket;
        }

        public void enqueueSocketPacket(final MySocketPacket socketPacket) {
            try {
                getSendingPacketQueue().put(socketPacket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        protected LinkedBlockingQueue<MySocketPacket> getSendingPacketQueue() {
            if (sendingPacketQueue == null) {
                sendingPacketQueue = new LinkedBlockingQueue<MySocketPacket>();
            }
            return sendingPacketQueue;
        }

        @Override
        public void run() {

            MySocketPacket packet;
            try {
                while (!Thread.interrupted() && mSocket.isConnected() &&
                        (packet = getSendingPacketQueue().take()) != null) {

                    InetAddress address = mSocket.getInetAddress();

                    OutputStream out = mSocket.getOutputStream();
                    out.write(packet.getData());
                    out.flush();
                    Writer myout = new OutputStreamWriter(out);
                    myout.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            Message msg = mClienthandler.obtainMessage();
            msg.what = 0x02;
            mClienthandler.sendMessage(msg);
            Log.i("aaaa", "SendRunnable quit");
        }
    }


    public class MyCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 0x01:
                    Sendrunnale.enqueueSocketPacket(new MySocketPacket("11".getBytes(), 0x1));
                    Message mymsg = mClienthandler.obtainMessage();
                    mymsg.what = 0x01;

                    mClienthandler.sendMessageDelayed(mymsg, 1000);
                    break;
                case 0x02:
                    if(bWorking == false) {
                        return false;
                    }
                    threadreceive.interrupt();
                    threadsend.interrupt();
                    try {
                        mySocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    myHandler.sendEmptyMessage(0x03);
                    mclientThread.quit();
                    synchronized(this) {
                        bWorking = false;
                    }
                    break;
            }
            return false;
        }
    }

    public void SendCMD(byte[] data) {
        if(Sendrunnale != null)
            ((clientSendRunnable) Sendrunnale).enqueueSocketPacket(new MySocketPacket(data, 0x02));
    }

}
